5.3 - Worker Commands- Gather and Build
Worker units are the engine of your economy and the architects of your base. They have two primary, specialized commands: gather
for harvesting resources and build
for constructing structures. While gathering is a simple "fire-and-forget" command, building requires a more deliberate, multi-step process.
The gather
Command: Your Economic Engine
The gather
command is the simplest and most fundamental worker action.
- Signature:
worker.gather(target)
- Target: A
Unit
object that is aMineralField
orVespeneGeyser
. - Behavior: The worker will travel to the resource, harvest it, return it to the nearest townhall, and continue this loop indefinitely. You only need to issue this command once per worker.
# A common pattern: find idle workers and put them to work.
for worker in self.workers.idle:
# Find the closest mineral patch to this specific worker.
closest_mineral_patch = self.mineral_field.closest_to(worker)
# Issue the gather command.
self.do(worker.gather(closest_mineral_patch))
The Build Request Lifecycle
Constructing a building is not a single command but a workflow. You must first ask the game for a valid location before you can command a worker to build there.
(You decide to build a Pylon)
|
v
.---- CAN I BUILD? -------------.
| - self.can_afford() |
| - self.already_pending() == 0 |
`-------------------------------`
| (Yes)
v
.---- WHERE TO BUILD? -------------------.
| location = await self.find_placement() |
`----------------------------------------`
| (Location is valid)
v
.---- ISSUE COMMAND ----------------.
| await self.build(PYLON, location) |
`-----------------------------------`
|
v
(Worker begins construction)
This lifecycle prevents errors and ensures your bot doesn't try to build in illegal locations or waste resources on redundant structures.
Key Functions for Building
Function | Signature | Role in the Lifecycle |
---|---|---|
self.can_afford(unit_id) | (UnitTypeId) -> bool | Condition Check. Returns True if you have enough minerals and gas for a given unit or structure. |
self.already_pending(unit_id) | (UnitTypeId) -> int or float | Condition Check. Returns the number of a given unit/structure type currently in production. Essential for avoiding duplicates. |
self.find_placement(...) | (UnitTypeId, near, ...) -> Point2 or None | Location Finding. Asks the game for a valid build spot. This is an async function and must be awaited. |
self.build(...) | (UnitTypeId, near_or_location) -> UnitCommand | Action Issuing. The high-level helper that finds a worker and issues the final build command. This is also async . |
Deep Dive: self.find_placement()
This is a powerful but potentially slow function. Understanding its parameters is key to using it effectively.
Parameter | Type | Description |
---|---|---|
building | UnitTypeId | Required. The specific building you want to place (e.g., UnitTypeId.PYLON ). |
near | Point2 | Required. The center point of the search area. |
max_distance | int | The maximum radius to search from the near point. Defaults to 20. |
placement_step | int | The grid size of the search. A higher value (e.g., 5) is faster but less precise. A lower value (e.g., 1) is slower but will find tighter spots. Defaults to 2. |
Code Example: The Architect Bot
This bot demonstrates a robust and scalable building system. Instead of reacting instantly, it uses a simple list as a "build queue" to manage its construction projects, tackling one at a time.
# architect_bot.py
from collections import deque
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.main import run_game
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import UnitTypeId
class ArchitectBot(BotAI):
"""A bot that uses a build queue to manage construction projects."""
def __init__(self):
super().__init__()
# Use a deque as a simple, efficient queue.
self.build_queue = deque()
async def on_step(self, iteration: int):
await self.distribute_workers()
self.update_build_queue()
await self.execute_build_queue()
def update_build_queue(self):
"""Adds required buildings to the queue."""
# Queue a Supply Depot if we are running low on supply.
if (
self.supply_left < 5
and self.already_pending(UnitTypeId.SUPPLYDEPOT) == 0
and UnitTypeId.SUPPLYDEPOT not in self.build_queue
):
self.build_queue.append(UnitTypeId.SUPPLYDEPOT)
# Queue a Barracks if we have a depot but no barracks.
if (
self.structures(UnitTypeId.SUPPLYDEPOT).ready.exists
and not self.structures(UnitTypeId.BARRACKS).exists
and self.already_pending(UnitTypeId.BARRACKS) == 0
and UnitTypeId.BARRACKS not in self.build_queue
):
self.build_queue.append(UnitTypeId.BARRACKS)
async def execute_build_queue(self):
"""Works on the first item in the build queue."""
if not self.build_queue:
return
# Get the next building project from the front of the queue.
building_to_construct = self.build_queue[0]
# Check if we can afford the project.
if self.can_afford(building_to_construct):
# Find a location for the building.
build_location = await self.find_placement(
building_to_construct, near=self.start_location, placement_step=5
)
if build_location:
# Issue the build command.
await self.build(building_to_construct, build_location)
# Remove the completed project from the queue.
self.build_queue.popleft()
if __name__ == "__main__":
run_game(
maps.get("BlackburnAIE"),
[
Bot(Race.Terran, ArchitectBot()),
Computer(Race.Zerg, Difficulty.VeryEasy)
],
realtime=True,
)